import socket, sys
# 导入 socket 模块，用于创建网络套接字，实现网络通信
# 导入 sys 模块，用于访问命令行参数

if len(sys.argv) != 3:
    # 检查命令行参数的数量是否不等于 3
    print("Usage: python gopherclient.py <host> <filename>")
    # 若不等于 3，打印使用说明，提示用户正确的命令行参数格式
    sys.exit(1)
    # 退出程序，返回状态码 1 表示程序异常退出

# sys.argv 的含义：
# 当你在命令行运行 Python 脚本时，sys.argv 会记录下你输入的所有内容。
# 例如，运行 python gopherclient.py example.com test.txt 时，sys.argv 列表内容为 
# ['gopherclient.py', 'example.com', 'test.txt']。

port = 70
# 定义 Gopher 协议默认使用的端口号为 70

host = sys.argv[1]
# 从命令行参数中获取第一个参数，作为要连接的 Gopher 服务器的主机名或 IP 地址

filename = sys.argv[2]
# 从命令行参数中获取第二个参数，作为要从服务器请求的文件名

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 创建一个 TCP 套接字对象
# socket.AF_INET 表示使用 IPv4 地址族
# socket.SOCK_STREAM 表示使用面向连接的 TCP 协议

s.connect((host, port))
# 尝试连接到指定主机和端口的 Gopher 服务器

# ==================== 原方案 ====================
# 将字符串编码为字节类型再发送
s.sendall((filename + "\r\n").encode('utf-8'))
# 将请求的文件名和换行符（\r\n）拼接成字符串
# 使用 encode('utf-8') 方法将字符串编码为字节类型
# 调用 sendall 方法将字节数据完整地发送到服务器

while True:
    # 进入一个无限循环，持续接收服务器返回的数据
    buf = s.recv(2048)
    # 从套接字接收最多 2048 字节的数据，并存储在变量 buf 中

    if not len(buf):
        # 如果接收到的数据长度为 0，说明服务器已经关闭连接
        break
        # 跳出循环

    # 将接收到的字节数据解码为字符串输出
    sys.stdout.write(buf.decode('utf-8'))

# ==================== 新方案（使用 makefile） ====================
# 以下是使用 makefile 函数的新方案，它能让网络套接字的读写操作更像操作文件
# 并且自动处理编码解码，代码更简洁易读

# # 重新创建套接字（如果要测试新方案，需取消注释以下代码并注释掉上面原方案的代码）
# s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# s.connect((host, port))

# # 使用 makefile 创建读写文件对象
# sock_file = s.makefile('rw', encoding='utf-8')

# # 发送请求
# sock_file.write(filename + "\r\n")
# sock_file.flush()  # 确保数据发送

# # 接收响应
# while True:
#     line = sock_file.readline()
#     if not line:
#         break
#     sys.stdout.write(line)

# # 关闭文件对象和套接字
# sock_file.close()

# ==================== 通用操作 ====================
s.close()    